package ph.com.gs3.formalistics.model.valueobjects.business.form;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import ph.com.gs3.formalistics.global.utilities.JSONUtilities;
import ph.com.gs3.formalistics.global.utilities.logging.FLLogger;
import ph.com.gs3.formalistics.model.valueobjects.business.document.Document;
import ph.com.gs3.formalistics.model.valueobjects.business.document.DocumentAction;
import ph.com.gs3.formalistics.model.valueobjects.business.form.FormField.InvalidFormFieldException;
import android.annotation.SuppressLint;

@SuppressLint("DefaultLocale")
public class Form {

	public static final String TAG = Form.class.getSimpleName();

	private String dbId;	// id of the form when stored here in the device
	private String webId;	// form id
	private String server;
	private String companyId;
	private String name;
	private String workflowId;
	private String webTableName;

	private String rawFormFields;	// JSONArray
	private String rawOnCreateFieldsEnabled;	// JSONArray
	private String rawOnCreateFieldsRequired;	// JSONArray
	private String rawOnCreateFieldsHidden;		// JSONArray
	private String rawButtons;	// CSV
	private String rawActions;	// JSONArray

	private List<FormField> fields;
	private List<String> onCreateFieldsEnabled;		// JSONArray
	private List<String> onCreateFieldsRequired;	// JSONArray
	private List<String> onCreateFieldsHidden;		// JSONArray
	private List<String> buttons;
	private List<DocumentAction> actions;

	private String documentsLastUpdatedDate;
	private String commentsLastUpdateDate;

	// ========================================================================
	// Builders / Mappers

	public static Form createFromJSON(JSONObject rawJSON, String server)
	        throws InvalidFormException {

		Form form = new Form();

		form.server = server;

		try {
			//	@formatter:off
			form = form.mapBasicFields(rawJSON)
					.mapFormFields(rawJSON)
					.mapRaws(rawJSON)
					.mapFieldProperties(rawJSON)
					.mapActionsAndButtons(rawJSON);
			//	@formatter:on

		} catch (JSONException e) {
			throw new InvalidFormException(rawJSON.toString(), e);
		} catch (InvalidFormFieldException e) {			
			e.printStackTrace();
			throw new InvalidFormException(e);
		}

		return form;

	}

	public static List<FormField> createFormFieldsFromJSONArray(JSONArray formFieldsJSONArray)
	        throws InvalidFormFieldException, JSONException {

		List<FormField> fields = new ArrayList<FormField>();
		for (int i = 0; i < formFieldsJSONArray.length(); i++) {
			// throws InvalidFormFieldException
			FormField formField = FormField.createFromJSON(formFieldsJSONArray.getJSONObject(i));
			if (formField != null) {
				fields.add(formField);
			} else {
				FLLogger.e(TAG, "Failed to create field from: "
				        + formFieldsJSONArray.getJSONObject(i).toString());
				throw new InvalidFormFieldException("Failed to create field from: "
				        + formFieldsJSONArray.getJSONObject(i).toString());
			}
		}

		return fields;

	}

	public Form mapBasicFields(JSONObject rawJSON) throws JSONException {
		// @formatter:off
		companyId 		= rawJSON.getString("company_id");
		webId 			= rawJSON.getString("form_id");
		name	 		= rawJSON.getString("form_name");
		workflowId 		= rawJSON.getString("workflow_id");
		webTableName 	= rawJSON.getString("form_table_name");
		// @formatter:on

		return this;
	}

	public Form mapFormFields(JSONObject rawJSON) throws InvalidFormFieldException, JSONException {

		JSONObject fieldsJSON = new JSONObject(rawJSON.getString("mobile_fields"));
		JSONArray fieldsJSONArray = fieldsJSON.getJSONArray("fields");

		fields = Form.createFormFieldsFromJSONArray(fieldsJSONArray);
		return this;
	}

	public Form mapRaws(JSONObject rawJSON) throws JSONException {

		// @formatter:off
		rawFormFields 				= rawJSON.getString("mobile_fields");
		rawOnCreateFieldsEnabled 	= rawJSON.getString("on_create_fields_enabled");
		rawOnCreateFieldsRequired 	= rawJSON.getString("on_create_fields_required");
		rawOnCreateFieldsHidden 	= rawJSON.getString("on_create_fields_hidden");
		rawButtons				 	= rawJSON.getString("buttons");
		rawActions					= rawJSON.getString("on_create_actions");
		
		// validation and cleanup
		// all null json arrays should be converted to
		
		rawOnCreateFieldsEnabled	= JSONUtilities.cleanupRawJSONArray(rawOnCreateFieldsEnabled);
		rawOnCreateFieldsRequired	= JSONUtilities.cleanupRawJSONArray(rawOnCreateFieldsRequired);
		rawOnCreateFieldsHidden		= JSONUtilities.cleanupRawJSONArray(rawOnCreateFieldsHidden);
		
		rawActions					= JSONUtilities.cleanupRawJSONArray(rawActions);
		
		// @formatter:on

		return this;

	}

	public Form mapFieldProperties(JSONObject rawJSON) throws JSONException {

		// get and convert fields enabled
		JSONArray fieldsEnabledJSONArray = JSONUtilities.getJSONArrayFromJSONObject(rawJSON,
		        "on_create_fields_enabled");

		onCreateFieldsEnabled = new ArrayList<String>();
		for (int i = 0; i < fieldsEnabledJSONArray.length(); i++) {
			onCreateFieldsEnabled.add(fieldsEnabledJSONArray.getString(i));
		}

		// get and convert fields required
		JSONArray fieldsRequiredJSONArray = JSONUtilities.getJSONArrayFromJSONObject(rawJSON,
		        "on_create_fields_required");

		onCreateFieldsRequired = new ArrayList<String>();
		for (int i = 0; i < fieldsRequiredJSONArray.length(); i++) {
			onCreateFieldsRequired.add(fieldsRequiredJSONArray.getString(i));
		}

		// get and convert fields hidden
		JSONArray fieldsHiddenJSONArray = JSONUtilities.getJSONArrayFromJSONObject(rawJSON,
		        "on_create_fields_hidden");

		onCreateFieldsHidden = new ArrayList<String>();
		for (int i = 0; i < fieldsHiddenJSONArray.length(); i++) {
			onCreateFieldsHidden.add(fieldsHiddenJSONArray.getString(i));
		}

		return this;

	}

	public Form mapActionsAndButtons(JSONObject rawJSON) throws JSONException {

		JSONArray actionsJSONArray = new JSONArray(rawJSON.getString("on_create_actions"));
		actions = Document.createActionsFromJSONArray(actionsJSONArray);

		String buttonsRaw = rawJSON.getString("buttons");
		buttons = new ArrayList<String>(Arrays.asList(buttonsRaw.split(",")));

		return this;

	}

	// ========================================================================
	// Getters

	@Override
	public String toString() {
		return name;
	}

	public String getDbId() {
		return dbId;
	}

	public void setDbId(String dbId) {
		this.dbId = dbId;
	}

	public String getWebId() {
		return webId;
	}

	public void setWebId(String webId) {
		this.webId = webId;
	}

	public String getServer() {
		return server;
	}

	public void setServer(String server) {
		this.server = server;
	}

	public String getCompanyId() {
		return companyId;
	}

	public void setCompanyId(String companyId) {
		this.companyId = companyId;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getWorkflowId() {
		return workflowId;
	}

	public void setWorkflowId(String workflowId) {
		this.workflowId = workflowId;
	}

	public String getWebTableName() {
		return webTableName;
	}

	public void setWebTableName(String webTableName) {
		this.webTableName = webTableName;
	}

	public String getRawFormFields() {
		return rawFormFields;
	}

	public void setRawFormFields(String rawFormFields) {
		this.rawFormFields = rawFormFields;
	}

	public String getRawOnCreateFieldsEnabled() {
		return rawOnCreateFieldsEnabled;
	}

	public void setRawOnCreateFieldsEnabled(String rawOnCreateFieldsEnabled) {
		this.rawOnCreateFieldsEnabled = rawOnCreateFieldsEnabled;
	}

	public String getRawOnCreateFieldsRequired() {
		return rawOnCreateFieldsRequired;
	}

	public void setRawOnCreateFieldsRequired(String rawOnCreateFieldsRequired) {
		this.rawOnCreateFieldsRequired = rawOnCreateFieldsRequired;
	}

	public String getRawOnCreateFieldsHidden() {
		return rawOnCreateFieldsHidden;
	}

	public void setRawOnCreateFieldsHidden(String rawOnCreateFieldsHidden) {
		this.rawOnCreateFieldsHidden = rawOnCreateFieldsHidden;
	}

	public String getRawButtons() {
		return rawButtons;
	}

	public void setRawButtons(String rawButtons) {
		this.rawButtons = rawButtons;
	}

	public String getRawActions() {
		return rawActions;
	}

	public void setRawActions(String rawActions) {
		this.rawActions = rawActions;
	}

	public List<FormField> getFields() {
		return fields;
	}

	public void setFields(List<FormField> fields) {
		this.fields = fields;
	}

	public List<String> getOnCreateFieldsEnabled() {
		return onCreateFieldsEnabled;
	}

	public void setOnCreateFieldsEnabled(List<String> onCreateFieldsEnabled) {
		this.onCreateFieldsEnabled = onCreateFieldsEnabled;
	}

	public List<String> getOnCreateFieldsRequired() {
		return onCreateFieldsRequired;
	}

	public void setOnCreateFieldsRequired(List<String> onCreateFieldsRequired) {
		this.onCreateFieldsRequired = onCreateFieldsRequired;
	}

	public List<String> getOnCreateFieldsHidden() {
		return onCreateFieldsHidden;
	}

	public void setOnCreateFieldsHidden(List<String> onCreateFieldsHidden) {
		this.onCreateFieldsHidden = onCreateFieldsHidden;
	}

	public List<String> getButtons() {
		return buttons;
	}

	public void setButtons(List<String> buttons) {
		this.buttons = buttons;
	}

	public List<DocumentAction> getActions() {
		return actions;
	}

	public void setActions(List<DocumentAction> actions) {
		this.actions = actions;
	}

	// ========================================================================
	// Exceptions

	public String getDocumentsLastUpdatedDate() {
		return documentsLastUpdatedDate;
	}

	public void setDocumentsLastUpdatedDate(String documentsLastUpdatedDate) {
		this.documentsLastUpdatedDate = documentsLastUpdatedDate;
	}

	public String getCommentsLastUpdateDate() {
		return commentsLastUpdateDate;
	}

	public void setCommentsLastUpdateDate(String commentsLastUpdateDate) {
		this.commentsLastUpdateDate = commentsLastUpdateDate;
	}

	public static class InvalidFormException extends Exception {

		private static final long serialVersionUID = 1843266962876331389L;

		private String message;

		// public InvalidFormException(String rawJSONString, Exception e) {
		// super(e);
		//
		// message =
		// "The application failed to create a form from the raw JSON provided: "
		// + rawJSONString;
		// }

		public InvalidFormException(String message, Throwable throwable) {
			super(message, throwable);
		}

		public InvalidFormException(Throwable throwable) {
			super(throwable);
		}

		@Override
		public String getMessage() {
			return message;
		}

	}

}
